using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Reflection;

namespace GNetDesktop
{
    //SingleProgamInstance uses a mutex synchronization object
    // to ensure that only one copy of process is running at
    // a particular time.  It also allows for UI identification
    // of the intial process by bring that window to the foreground.
    public class SingleProgramInstance : IDisposable
    {

        //Win32 API calls necesary to raise an unowned processs main window
        [DllImport("user32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);
        [DllImport("user32.dll")]
        private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
        [DllImport("user32.dll")]
        private static extern bool IsIconic(IntPtr hWnd);

        private const int SW_RESTORE = 9;
        private const int SW_SHOW = 5;

        //private members 
        private Mutex _processSync;
        private bool _owned = false;



        private const int INVALID_HANDLE_VALUE = -1;
        private const int FILE_MAP_WRITE = 0x2;
        private const int FILE_MAP_READ = 0x0004;

        [DllImport("kernel32.dll", EntryPoint = "OpenFileMapping",
                      SetLastError = true, CharSet = CharSet.Auto)]
        private static extern IntPtr OpenFileMapping(int
            wDesiredAccess, bool bInheritHandle, String lpName);

        [DllImport("Kernel32.dll", EntryPoint = "CreateFileMapping",
                         SetLastError = true, CharSet = CharSet.Auto)]

        private static extern IntPtr CreateFileMapping(uint hFile,
                IntPtr lpAttributes, uint flProtect,
                uint dwMaximumSizeHigh, uint dwMaximumSizeLow,
                string lpName);

        [DllImport("Kernel32.dll")]
        private static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject,
                uint dwDesiredAccess, uint dwFileOffsetHigh,
                uint dwFileOffsetLow, uint dwNumberOfBytesToMap);

        [DllImport("Kernel32.dll", EntryPoint = "UnmapViewOfFile",
                SetLastError = true, CharSet = CharSet.Auto)]
        private static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);

        [DllImport("kernel32.dll", EntryPoint = "CloseHandle",
                SetLastError = true, CharSet = CharSet.Auto)]
        private static extern bool CloseHandle(uint hHandle);
        [DllImport("kernel32.dll", EntryPoint = "GetLastError",
                SetLastError = true, CharSet = CharSet.Auto)]

        private static extern uint GetLastError();
        //private IntPtr memoryFileHandle;

        public enum FileAccess : int
        {
            ReadOnly = 2,
            ReadWrite = 4
        }

        public IntPtr CreateMMF(string fileName)
        {


            IntPtr memoryFileHandle = CreateFileMapping(0xFFFFFFFF,
                IntPtr.Zero,  (uint)FileAccess.ReadWrite, 0, 8, fileName);


            IntPtr mappedViewHandle = MapViewOfFile(memoryFileHandle,
               (uint)FILE_MAP_WRITE, 0, 0, 8);

            Marshal.WriteIntPtr(mappedViewHandle, IntPtr.Zero);

            UnmapViewOfFile(mappedViewHandle);
            CloseHandle((uint)mappedViewHandle);


            return memoryFileHandle;
        }

        public void WriteHandle(string fileName,IntPtr windowHandle)
        {
            IntPtr memoryFileHandle =
              OpenFileMapping(FILE_MAP_WRITE, false, fileName);

            IntPtr mappedViewHandle = MapViewOfFile(memoryFileHandle,
                (uint)FILE_MAP_WRITE, 0, 0, 8);

            Marshal.WriteIntPtr(mappedViewHandle, windowHandle);

            UnmapViewOfFile(mappedViewHandle);
            CloseHandle((uint)mappedViewHandle);
        }

        public IntPtr ReadHandle(string fileName)
        {
            IntPtr memoryFileHandle =
              OpenFileMapping(FILE_MAP_READ, false, fileName);


            IntPtr mappedViewHandle = MapViewOfFile(memoryFileHandle,
                                                (uint)FILE_MAP_READ, 0, 0, 8);

            IntPtr windowHandle = Marshal.ReadIntPtr(mappedViewHandle);
            if (windowHandle == IntPtr.Zero)
                throw new ArgumentException("Reading from the specified" +
                                     " address in  Shared Memory failed.");

            UnmapViewOfFile(mappedViewHandle);
            CloseHandle((uint)mappedViewHandle);
            return windowHandle;
        }



        public SingleProgramInstance()
        {
            //Initialize a named mutex and attempt to
            // get ownership immediatly 
            _processSync = new Mutex(
             true, // desire intial ownership
             Assembly.GetExecutingAssembly().GetName().Name,
             out _owned);
        }

        public SingleProgramInstance(string identifier)
        {
            //Initialize a named mutex and attempt to
            // get ownership immediately.
            //Use an addtional identifier to lower
            // our chances of another process creating
            // a mutex with the same name.
            _processSync = new Mutex(
             true, // desire intial ownership
             Assembly.GetExecutingAssembly().GetName().Name + identifier,
             out _owned);
        }

        ~SingleProgramInstance()
        {
            //Release mutex (if necessary) 
            //This should have been accomplished using Dispose() 
            Release();
        }

        public bool IsSingleInstance
        {
            //If we don't own the mutex than
            // we are not the first instance.
            get { return _owned; }
        }

        public void RaiseOtherProcess()
        {
            Process proc = Process.GetCurrentProcess();
            // Using Process.ProcessName does not function properly when
            // the name exceeds 15 characters. Using the assembly name
            // takes care of this problem and is more accruate than other
            // work arounds.
            string assemblyName = Assembly.GetExecutingAssembly().GetName().Name;
            foreach (Process otherProc in Process.GetProcessesByName(assemblyName))
            {
                //ignore this process
                if (proc.Id != otherProc.Id)
                {
                    // Found a "same named process".
                    // Assume it is the one we want brought to the foreground.
                    // Use the Win32 API to bring it to the foreground.
                    IntPtr hWnd = otherProc.MainWindowHandle;

                    if (hWnd != System.IntPtr.Zero)
                    {
                        if (IsIconic(hWnd))
                        {
                            ShowWindowAsync(hWnd, SW_RESTORE);
                            
                        }
                    }
                    else
                    {
                        hWnd = ReadHandle("Local" + "\\sharedMemoryGnetIBD_771230");
                        
                        ShowWindowAsync(hWnd, SW_SHOW);
                        ShowWindowAsync(hWnd, SW_RESTORE);
                    }

                    
                    SetForegroundWindow(hWnd);
                    return;
                }
            }
        }

        private void Release()
        {
            if (_owned)
            {
                //If we owne the mutex than release it so that
                // other "same" processes can now start.
                _processSync.ReleaseMutex();
                _owned = false;
            }
        }

        #region Implementation of IDisposable
        public void Dispose()
        {
            //release mutex (if necessary) and notify 
            // the garbage collector to ignore the destructor
            Release();
            GC.SuppressFinalize(this);
        }
        #endregion
    }


}
